package it.uniroma2.dtk.dt;

import it.uniroma2.dtk.common.Spectrum;
import it.uniroma2.util.math.ArrayMath;
import it.uniroma2.util.tree.Tree;

public abstract class AbstractPartialDT extends DefaultAbstractDT {

	public AbstractPartialDT(int randomOffset, int vectorsSize, boolean usePos, boolean lexicalized) throws Exception {
		super(randomOffset, vectorsSize, usePos, lexicalized);
	}
	
	double lambda = 1;
	double muSq = 1;
	
	@Override
	public void setLambda(double lambda) {
		super.setLambda(lambda);
		this.lambda = lambda;
	};
	
	@Override
	public double getLambda() {
		return lambda;
	};
	
	public void setMu(double mu) {
		muSq = Math.sqrt(mu);
	};
	
	public double getMu() {
		return muSq*muSq;
	};
	
	protected double[] sRecursive(Tree node, Spectrum sum) throws Exception {
		double[] result = ArrayMath.scalardot(lambda, getLabelVector(node));
		if (!node.isTerminal()) {
			//s_p(node) must sum s_p^I(node) for each of the 2^|children|-1 indices subsets I 
			double[][] sums = new double[(int) (Math.pow(2, node.getChildren().size())-1)][vectorSize];
			for (int i=0; i<sums.length; i++)
				sums[i] = null;
			for (int i=0; i<node.getChildren().size(); i++) {
				Tree child = node.getChildren().get(i);
				double[] childVector = sRecursive(child, sum);
				for (int j=0; j<sums.length; j++) {
					//Current index i only appears in subset I number j if ((j+1) % 2^(i+1)) / 2^i = 1
					//To see this, consider subset I as a binary number
					if (((j+1) % ((int)Math.pow(2, i+1))) / ((int)Math.pow(2, i)) == 1)
						sums[j] = (sums[j] == null) ? childVector : op(sums[j], childVector);
				}
			}
			for (int j=0; j<sums.length; j++) {
				//This can be surely made more efficient...
				result = vectorComposer.sum(result, ArrayMath.scalardot(Math.pow(lambda, d(j+1)), sums[j]));
			}
			sum.setVector(vectorComposer.sum(sum.getVector(), result));
		}
		return result;
	}
	
	private int d(int i) {
		int max = 0;
		int min = 0;
		int c = 0;
		while (i > 0) {
			c++;
			if (i % 2 == 1) {
				if (min == 0)
					min = c;
				max = c;
			}
			i = i / 2;
		}
		return max-min;
	}

}
